package com.yeskery.nut.extend.asypt;

import com.yeskery.nut.application.NutApplication;
import com.yeskery.nut.core.Environment;
import com.yeskery.nut.core.NutException;
import com.yeskery.nut.core.Order;
import com.yeskery.nut.plugin.BindContext;
import com.yeskery.nut.plugin.NutApplicationSupportBasePlugin;
import com.yeskery.nut.plugin.PriorityPlugin;
import com.yeskery.nut.util.RsaUtils;
import com.yeskery.nut.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 该插件用于处理配置文件解密处理，因为修改系统配置的方式属于系统前置操作，该插件需要通过注解方式使用或通过重新构造一个{@link NutApplication}方式来启动应用，
 * 通过应用上下文注入的方式将无法解密。
 * 示例：
 * <pre>
 *     “@NutBootApplication(enablePlugins = AsyptPlugin.class)”
 * </pre>
 * <b>注意</b>：将需要配置的值提前调用{@link #encrypt(String)}方法生成加密字符串，然后在生成的加密串后添加<code>{asypt}:</code>前缀标识。
 * 示例：
 * <pre>
 *     原始数据：
 *     key=123456
 *     加密后的数据：
 *     key={asypt}:YbayV3nwjKcp0tmFZgNZ1WSy4iJdwX0TaPYzmqtq189SW7nWos58hbBjqnxhULeUQJeXsT52nKRE/m1cLUFEMa78kBUiaO0TqbiLxYE6z77+hiXX47eBEMpT7tpsMt9yjw28dtt8sZfa/S7iIPx0mAY9/Clbe6JQQDyK6WjNnb1PtG+nv7vjrWA89hifsfLx0TZFH+JiTFF6KnXci97r+GL5WjsF5YK48/YMp+Xjj9ngoaJzHj7Bqh+eCyiXN6mnyE6dsa+Hf0zzW8iaCmjkKsdRNfZnjANYGPfCxHaghrqxG3GtflEx0cfpNYlvSKI94zDxOa/ucmdg+ICi/+aUfA==
 * </pre>
 * @author sunjay
 * 2023/10/9
 */
public class AsyptPlugin extends NutApplicationSupportBasePlugin implements PriorityPlugin {
    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(AsyptPlugin.class.getName());
    /** 公钥字符串 */
    private static final String PUBLIC_KEY_STR = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgflTwurGVI8sGcHaptFfR8utxxFXvvXP2MkpZHFm3/b7oJ7/mOJ3t56lpNmzC0DtdpMBwOWXnTTcF7e8TZkSF5yjJIcsYlbEvFM8TKcOBcQsVP5/KUyI0atOj0oX+8irceuMCwi2z13lG25KdSnyVmRggcmCu0z0KjbpgI+l+knMrfBIHfZBQSXPJSATBsfzBStv5+i/5K4gOUtf/rP+SZvG5BK3m9ASHR36XU7HmboXIbT/FjWddwsBJK3MPC5ZYU0y+sav4x2zek7YhtBcvRttQDm26hHHPxtJmuksyBZw989YQkpNa16b6yoiVpOIhGfBMpfBbjTIzdW9gLetwIDAQAB";
    /** 私钥字符串 */
    private static final String PRIVATE_KEY_STR = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCuB+VPC6sZUjywZwdqm0V9Hy63HEVe+9c/YySlkcWbf9vugnv+Y4ne3nqWk2bMLQO12kwHA5ZedNNwXt7xNmRIXnKMkhyxiVsS8UzxMpw4FxCxU/n8pTIjRq06PShf7yKtx64wLCLbPXeUbbkp1KfJWZGCByYK7TPQqNumAj6X6Scyt8Egd9kFBJc8lIBMGx/MFK2/n6L/kriA5S1/+s/5Jm8bkEreb0BIdHfpdTseZuhchtP8WNZ13CwEkrcw8LllhTTL6xq/jHbN6TtiG0Fy9G21AObbqEcc/G0ma6SzIFnD3z1hCSk1rXpvrKiJWk4iEZ8Eyl8FuNMjN1b2At63AgMBAAECggEAHmGQfZEq6Hu7zovd9pVutx4neJ392kwa0p0LO8BMAA59JZEv9kQIQJkUS48mBgThvfCDD0JoEMtSUi6tuuxk62FZnAKx8ahp9s+bQAw2y2/zYRGo9ZsdOp1PBnjAwKatpirS1OQPnbiIVKgnCA9wzyzMwydeU8//GLExzzRbGwe11+R5ggm+3vxcXUDxzuoi3uWGdqiB54o6yp3LWTno7KUcOCeBKlz74yN87kNIwlEV2sef8+gQnAY25l4clUtAVpxE0febFBfMOKIsqjg9CA94oRroaKxBrOz09N8hmZV/HuSDVkNNeZgGS8xfc8Hm+Da59myNv7aQ/kDA2Z15kQKBgQDpVRPvo7xVDWF1hkUKxQsETL1Jkhu7kb5hCkZu/gvuuVTYaomVmGIxpI+WfrDnh5w2xWTId2fdVB9G4LJFxWJRdbogGC/KWoB64i46SVc8pTEYlEjweygHh/TyQOJW5/PFRPe96Ltoyxm5v/4TEJeSw8d39elzbIHPZIIx6DK0LwKBgQC+8ADTMt1fNcqpYmrHROB/LidSATCQf514Fyy9rd+G3I85tvZBPIQAKRDNazWopdqky2ROKh46Q+6MYp4q3E+bEXR/8YBaZ0PILbrXSkkDnHVLC72W7obdIAj7W/JHq0liV9g10HMUgaKijNm38yARIbQCBeT3aCkPDHf9X6fz+QKBgQCQpRglv3f5tcDL7T+RrJLmyHezSur8PR4q5AbDuaNGCwfo3SqLXRrKMXx9y+34qZ2Sao3OpA70Krj05Eg/blxDqVqHmNbPd4kcJGiJ43s78h/cX9hTk9U9ze9v2f815ra8fMOjq8m57eX1u2pTAT8lUU6vM/fTgNl76o+3Rn1gKwKBgQCW53B/eu/i08y8WoAxxYn4ei2xbidHzNRTmV1O/CBxodfAsY2muYFJ7yrMbzMVex92pxk7faYuPopGgZqoHT79O6prhB8BVCQKDbphdOCH7hv1l+3bX+FO2oa+wkiVxBZXlz3WKk0Jo/s5pUBdVdW+NHjkMGxp7PSmZCBhLDy9YQKBgQDMUpVfatAWZ7FhJUSsYmL6uqoIFXppgHp/iIhjG5fyZoiwLLY2kiuGDDQUmmdwydcHhVo37n05Sn9y7wZyPPRU9AuS6M9DVD1taJgH5LxIgHUKZoDVyQCj5ifnrXio63Hz8o4pqppcra5huz9O+ndk2b6DfQ8/T8cDmSsZdi39+w==";
    /** 加密前缀 */
    private static final String ASYPT_PREFIX = "{asypt}:";
    /** 公钥 */
    private final PublicKey publicKey;
    /** 私钥 */
    private final PrivateKey privateKey;
    /** 加密前缀 */
    private final String asyptPrefix;
    /** 环境对象 */
    private Environment environment;

    /**
     * 构建解密插件
     * @param publicKey 公钥
     * @param privateKey 私钥
     * @param asyptPrefix 加密前缀
     */
    public AsyptPlugin(PublicKey publicKey, PrivateKey privateKey, String asyptPrefix) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
        if (!StringUtils.isEmpty(asyptPrefix) && asyptPrefix.trim().isEmpty()) {
            throw new IllegalArgumentException("Parameter[asyptPrefix] Can Not Be Empty.");
        }
        this.asyptPrefix = asyptPrefix;
    }

    /**
     * 构建解密插件
     * @param publicKey 公钥
     * @param privateKey 私钥
     * @param asyptPrefix 加密前缀
     */
    public AsyptPlugin(String publicKey, String privateKey, String asyptPrefix) {
        try {
            this.publicKey = RsaUtils.loadPublicKey(publicKey);
            this.privateKey = RsaUtils.loadPrivateKey(privateKey);
            if (!StringUtils.isEmpty(asyptPrefix) && asyptPrefix.trim().isEmpty()) {
                throw new IllegalArgumentException("Parameter[asyptPrefix] Can Not Be Empty.");
            }
            this.asyptPrefix = asyptPrefix;
        } catch (Exception e) {
            throw new NutException("RSA Key Load Error.", e);
        }
    }

    /**
     * 构建解密插件
     * @param publicKey 公钥
     * @param privateKey 私钥
     */
    public AsyptPlugin(PublicKey publicKey, PrivateKey privateKey) {
        this(publicKey, privateKey, ASYPT_PREFIX);
    }

    /**
     * 构建解密插件
     * @param publicKey 公钥
     * @param privateKey 私钥
     */
    public AsyptPlugin(String publicKey, String privateKey) {
        this(publicKey, privateKey, ASYPT_PREFIX);
    }

    /**
     * 构建解密插件
     */
    public AsyptPlugin() {
        this(PUBLIC_KEY_STR, PRIVATE_KEY_STR);
    }

    /**
     * 获取加密后的文本
     * @param plainText 加密前的文本
     * @return 加密后的文本
     */
    public String encrypt(String plainText) {
        try {
            return Base64.getEncoder().encodeToString(RsaUtils.encrypt(plainText, publicKey));
        } catch (Exception e) {
            throw new NutException("RSA Encrypt Error.", e);
        }
    }

    @Override
    public void bind(BindContext bindContext) {
        Properties properties = environment.getEnvProperties();
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            String value = (String) entry.getValue();
            if (!StringUtils.isEmpty(value) && value.startsWith(asyptPrefix) && value.length() > asyptPrefix.length()) {
                value = value.substring(ASYPT_PREFIX.length());
                try {
                    value = new String(RsaUtils.decrypt(Base64.getDecoder().decode(value), privateKey), StandardCharsets.UTF_8);
                    properties.put(entry.getKey(), value);
                } catch (Exception e) {
                    logger.log(Level.WARNING, "Environment Property Key[" + entry.getKey() + "] Decrypt Fail, Original Property Value[" + entry.getValue() + "].");
                }
            }
        }
    }

    @Override
    public void setNutApplication(NutApplication nutApplication) {
        environment = nutApplication.getApplicationContext().getBean(Environment.class);
    }

    @Override
    public int getOrder() {
        return Order.MAX;
    }
}
